from typing import List

import tree_sitter_rust as tsrust
from tree_sitter import Language, Parser, Tree

from chernc.parser.tree_sitter_utils import has_error


def check_grammar(rust_code: str) -> bool:
    RUST_LANGUAGE = Language(tsrust.language())
    parser = Parser(RUST_LANGUAGE)
    tree = parser.parse(rust_code.encode(), encoding="utf8")
    return has_error(tree.root_node)


def parse_rust(rust_code: str) -> Tree:
    RUST_LANGUAGE = Language(tsrust.language())
    parser = Parser(RUST_LANGUAGE)
    tree = parser.parse(rust_code.encode(), encoding="utf8")
    return tree


def rust_code_chop(rust_code: str) -> List[str]:
    tree_node = parse_rust(rust_code).root_node
    children = tree_node.children
    res = []
    for n in children:
        if n.type == "ERROR":
            continue
        elif n.type == "expression_statement":
            continue
        elif n.type == "mod_item":
            continue
        elif n.type == "impl_item":
            if n.child_by_field_name("body") is not None:
                res.extend(
                    [
                        nc.text.decode("utf-8")
                        for nc in n.child_by_field_name("body").children
                        if nc.type != "{" and nc.type != "}"
                    ]
                )
        else:
            res.append(n.text.decode("utf-8"))
    return res


if __name__ == "__main__":
    rust_code = """
// In Rust, we don't use preprocessor macros like #define for header guards.
// Instead, we use the `mod` and `pub mod` keywords to organize modules.
// Here, we assume that `ALLOC_TESTING_H` is a header guard.

// If this is a header guard, it would typically be replaced by a module structure in Rust.
// For example:
static const tests: [Option<UnitTestFunction>; 10] = [
    Some(test_arraylist_new_free),
    Some(test_arraylist_append),
    Some(test_arraylist_prepend),
    Some(test_arraylist_insert),
    Some(test_arraylist_remove),
    Some(test_arraylist_remove_range),
    Some(test_arraylist_index_of),
    Some(test_arraylist_clear),
    Some(test_arraylist_sort),
    None,
];
{
    #[allow(non_upper_case_globals)]
    const malloc: fn() = alloc_test_malloc;
}
// In alloc_testing.rs or alloc_testing/mod.rs
pub mod alloc_testing {
    // Your code here

}
pub fn alloc_test_get_header(ptr: *const u8) -> *mut BlockHeader {
    let result = unsafe { (ptr as *mut BlockHeader).sub(1) }; 
    unsafe {
        assert!(result.read().magic_number == ALLOC_TEST_MAGIC);
    }
    result
}
struct 

union MyUnion, salfkjawtihl {
    afawf, ,f1: u32,
    f2: f32,
}
let u: int = 1;
/**/
// In main.rs or lib.rs
mod alloc_testing;
macro_rules! strdup {
    ($s:expr) => {
        alloc_test_strdup($s)
    };
}
#[allow(non_upper_case_globals)]
const malloc: fn() = alloc_test_malloc;
const FREE: fn() = alloc_test_free;
extern "C" {
    fn alloc_test_realloc(ptr: *mut c_void, size: usize) -> *mut c_void;
}
extern "C" {
    fn alloc_test_calloc(num: usize, size: usize) -> *mut libc::c_void;
}
extern "C" fn callback(target: *mut RustObject, a: i32) {
    println!("I'm called from C with value {0}", a);
    unsafe {
        // Update the value in RustObject with the value received from the callback:
        (*target).a = a;
    }
}
// No direct equivalent in Rust for C preprocessor macros like #define.
// However, you can use a constant or a module to achieve similar results.

// Example of using a constant:
const ALLOC_TESTING_C: &str = "";

// Example of using a module:
mod alloc_testing_c {
    // Placeholder for the contents of the module
}
const FREE_PATTERN: u32 = 0xDEADBEEF;
pub mod test_framework;
type UnitTestFunction = fn();
// Define the header for the ArrayList algorithm
pub mod algorithm_arraylist;
// No direct equivalent in Rust for C preprocessor macros, but we can use constants or functions
static mut var1:u8;
const ALGORITHM_COMPARE_INT_H: &str = "";
static mut variable1: i32;
static mut variable2: i32;
static mut variable3: i32;
static mut variable4: i32;
fn int_equal(vlocation1: *const std::ffi::c_void, vlocation2: *const std::ffi::c_void) -> bool {
    let location1: *const i32 = vlocation1 as *const i32;
    let location2: *const i32 = vlocation2 as *const i32;

    unsafe { *location1 == *location2 }
}

fn int_compare(vlocation1: *const std::ffi::c_void, vlocation2: *const std::ffi::c_void) -> i32 {
    let location1: *const i32 = vlocation1 as *const i32;
    let location2: *const i32 = vlocation2 as *const i32;

    unsafe {
        if *location1 < *location2 {
            -1
        } else if *location1 > *location2 {
            1
        } else {
            0
        }
    }
}
const Tk: usize = 0;
const Hash: usize = 1;
const Name: usize = 2;
const Class: usize = 3;
const Type: usize = 4;
const Val: usize = 5;
const HClass: usize = 6;
const HType: usize = 7;
const HVal: usize = 8;
const Idsz: usize = 9;

enum Type {
    CHAR,
    INT,
    PTR,
}
let variable1: i32;
let variable2: i32;
let variable3: i32;
let variable4: i32;
struct HashTableIterator {
	hash_table: *HashTable,
	next_entry: *HashTableEntry,
	next_chain: u32,
};
fn hash_table_iter_has_more(iterator: *mut HashTableIterator) -> bool {
	(*iterator).next_entry != std::ptr::null_mut()
}
#[repr(u8)]
enum Enum {
    Unit = 3,
    Tuple(u16),
    Struct {
        a: u8,
        b: u16,
    } = 1,
}

#[repr(C)]
struct BlockHeader {
    magic_number: u32,
    bytes: usize,
}
const MALLOC_PATTERN: u32 = 0xBAADF00D;
static mut allocation_limit: i32 = -1;
fn alloc_test_set_limit(alloc_count: i32);
fn alloc_test_set_limit(alloc_count: i32) {
    unsafe {
        allocation_limit = alloc_count;
    }
}

static mut allocated_bytes: usize = 0;
const ALLOC_TEST_MAGIC: u32 = 0x72ec82d2;

fn alloc_test_overwrite(ptr: *mut u8, length: usize, pattern: u32) {
    let byte_ptr = ptr as *mut u8;

    for i in 0..length {
        let pattern_seq = (i & 3) as i32;
        let b = ((pattern >> (8 * pattern_seq)) & 0xff) as u8;
        unsafe {
            *byte_ptr.add(i) = b;
        }
    }
}

fn alloc_test_get_allocated() -> usize {
    unsafe { allocated_bytes }
}

fn run_test(test: UnitTestFunction) {
    // Turn off any allocation limits that may have been set by a previous test.
    alloc_test_set_limit(-1);

    // Run the test
    test();

    // Check that all memory was correctly freed back during the test.
    assert_eq!(alloc_test_get_allocated(), 0);
}

fn run_tests(tests: &[UnitTestFunction]) {
    for &test in tests {
        run_test(test);
    }
}
use std::ptr;
 use std::mem;
 use std::slice;
 use std::ffi::CString;
 use std::os::raw::{c_char, c_void};

 static mut allocation_limit: i32 = -1;
 static mut allocated_bytes: usize = 0;

 fn alloc_test_get_header(ptr: *mut c_void) -> *mut BlockHeader {
     let result = (ptr as *mut BlockHeader).offset(-1);
     unsafe {
         assert_eq!((*result).magic_number, ALLOC_TEST_MAGIC);
     }
     result
 }

 fn alloc_test_free(ptr: *mut c_void) {
     if ptr.is_null() {
         return;
     }

     let header = alloc_test_get_header(ptr);
     let block_size = unsafe { (*header).bytes };
     unsafe {
         assert!(allocated_bytes >= block_size);
         alloc_test_overwrite(ptr as *mut u8, block_size, FREE_PATTERN);
         (*header).magic_number = 0;
         libc::free(header as *mut c_void);
         allocated_bytes -= block_size;
     }
 }

 fn alloc_test_malloc(bytes: usize) -> *mut c_void {
     if unsafe { allocation_limit == 0 } {
         return ptr::null_mut();
     }

     let header = unsafe { libc::malloc(mem::size_of::<BlockHeader>() + bytes) as *mut BlockHeader };
     if header.is_null() {
         return ptr::null_mut();
     }

     unsafe {
         (*header).magic_number = ALLOC_TEST_MAGIC;
         (*header).bytes = bytes;
         let ptr = header.offset(1) as *mut c_void;
         alloc_test_overwrite(ptr as *mut u8, bytes, MALLOC_PATTERN);
         allocated_bytes += bytes;
         if allocation_limit > 0 {
             allocation_limit -= 1;
         }
         ptr
     }
 }

 fn alloc_test_realloc(ptr: *mut c_void, bytes: usize) -> *mut c_void {
     let new_ptr = alloc_test_malloc(bytes);
     if new_ptr.is_null() {
         return ptr::null_mut();
     }

     if !ptr.is_null() {
         let header = alloc_test_get_header(ptr);
         let bytes_to_copy = unsafe {
             let bytes_to_copy = (*header).bytes;
             if bytes_to_copy > bytes {
                 bytes
             } else {
                 bytes_to_copy
             }
         };
         unsafe {
             libc::memcpy(new_ptr, ptr, bytes_to_copy);
             alloc_test_free(ptr);
         }
     }

     new_ptr
 }

 fn alloc_test_calloc(nmemb: usize, bytes: usize) -> *mut c_void {
     let total_bytes = nmemb * bytes;
     let result = alloc_test_malloc(total_bytes);
     if result.is_null() {
         return ptr::null_mut();
     }

     unsafe {
         libc::memset(result, 0, total_bytes);
     }

     result
 }
/**/
fn alloc_test_strdup(string: *const c_char) -> *mut c_char;
 fn alloc_test_strdup(string: *const c_char) -> *mut c_char {
     let len = unsafe { libc::strlen(string) };
     let result = alloc_test_malloc(len + 1) as *mut c_char;
     if result.is_null() {
         return ptr::null_mut();
     }

     unsafe {
         libc::strcpy(result, string);
     }

     result
 }
use std::ptr;
use std::mem;
use std::cmp::Ordering;

type ArrayListValue = *mut ();
type ArrayListEqualFunc = fn(ArrayListValue, ArrayListValue) -> bool;
type ArrayListCompareFunc = fn(ArrayListValue, ArrayListValue) -> Ordering;
struct ArraryList;
struct ArrayList {
    data: *mut ArrayListValue,
    length: usize,
    _alloced: usize,
}
impl Set {
    type E = u8;
    const a:u8 = 1;
    fn num_entries(&self) -> u32 {
        self.entries
    }

    fn register_free_function(&mut self, free_func: SetFreeFunc) {
        self.free_func = free_func;
    }
}
"""
    rust_code2 = """for test in tests.iter().filter(|test| !test.is_null()) {
    run_test(test);
}"""
    for n in rust_code_chop(rust_code):
        print(n)
